﻿using System;
using System.Runtime.InteropServices;
using Pfz.DataTypes;

namespace Pfz.Caching
{
	/// <summary>
	/// This class is a GCHandle wrapper that allows you to reference other objects
	/// using WeakReference, Strong or other reference types.
	/// It has a dispose and a destructor, but it is NOT thread safe, in the sense
	/// that changing the type or disposing from many threads can cause exceptions.
	/// Even not being thread-safe, this class is abort-safe.
	/// </summary>
	public sealed class ThreadUnsafeReference<T>:
		IReference<T>,
		IEquatable<IReference<T>>,
		#if !WINDOWS_PHONE
			ICloneable<ThreadUnsafeReference<T>>,
		#endif
		IDisposable
	where
		T: class
	{
		private ThreadUnsafeReferenceSlim<T> _reference;

		/// <summary>
		/// Creates a new empty reference.
		/// </summary>
		public ThreadUnsafeReference():
			this(default(T))
		{
		}

		/// <summary>
		/// Creates a new reference using the given value and handle type.
		/// </summary>
		public ThreadUnsafeReference(T value, GCHandleType handleType=GCHandleType.Weak)
		{
			_reference = new ThreadUnsafeReferenceSlim<T>(value, handleType);
		}
		
		/// <summary>
		/// Frees the handle.
		/// </summary>
		~ThreadUnsafeReference()
		{
			_reference.Dispose();
		}
		
		/// <summary>
		/// Frees the handle immediatelly.
		/// </summary>
		public void Dispose()
		{
			_reference.Dispose();
			GC.SuppressFinalize(this);
		}
		
		/// <summary>
		/// Gets or sets the value pointed by the handle.
		/// </summary>
		public T Value
		{
			get
			{
				return _reference.Value;
			}
			set
			{
				_reference.Value = value;
			}
		}
		
		/// <summary>
		/// Gets or sets the handle type.
		/// </summary>
		public GCHandleType HandleType
		{
			get
			{
				return _reference.HandleType;
			}
			set
			{
				_reference.HandleType = value;
			}
		}

		/// <summary>
		/// Gets the data of this Reference.
		/// </summary>
		public ReferenceData<T> GetData()
		{
			return _reference.GetData();
		}

		/// <summary>
		/// Sets the data of this reference.
		/// </summary>
		public void Set(GCHandleType handleType, T value)
		{
			_reference.Set(handleType, value);
		}

		/// <summary>
		/// Gets the HashCode of the Value.
		/// </summary>
		public override int GetHashCode()
		{
			return _reference.GetHashCode();
		}

		/// <summary>
		/// Compares the Value of this reference with the value of another reference.
		/// </summary>
		public override bool Equals(object obj)
		{
			return _reference.Equals(obj);
		}

		/// <summary>
		/// Compares the Value of this reference with the value of another reference.
		/// </summary>
		public bool Equals(IReference<T> other)
		{
			return _reference.Equals(other);
		}
		
		#region IValueContainer Members
			object IValueContainer.Value
			{
				get
				{
					return _reference.Value;
				}
				set
				{
					_reference.Value = (T)value;
				}
			}
		#endregion
		#region ICloneable<ThreadUnsafeReference<T>> Members
			/// <summary>
			/// Creates a copy of this reference.
			/// </summary>
			public ThreadUnsafeReference<T> Clone()
			{
				return new ThreadUnsafeReference<T>(_reference.Value, _reference.HandleType);
			}
		#endregion
		#region ICloneable Members
			#if !WINDOWS_PHONE
			object ICloneable.Clone()
			{
				return Clone();
			}
			#endif
		#endregion
		#region IReference Members
			void IReference.Set(GCHandleType handleType, object value)
			{
				Set(handleType, (T)value);
			}
			IReferenceData IReference.GetData()
			{
				return GetData();
			}
		#endregion
	}
}
